package com.leonzhangxf.ioc.event.publication;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

/**
 * The demo of use application-context's event publish.
 *
 * @author leonzhangxf
 */
@Configuration
@EnableAsync
@ComponentScan({"com.leonzhangxf.ioc.event.publication"})
public class ApplicationContextForEventStarter {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(ApplicationContextForEventStarter.class);

        ApplicationContextForEventStarter starter = applicationContext.getBean(ApplicationContextForEventStarter.class);

        HelloWorldEvent helloWorldEvent = new HelloWorldEvent(starter, "hello leon");
        SayHelloEvent sayHelloEvent = new SayHelloEvent(starter, "say hello to leon");
        SayHelloEvent sayHelloEvent2 = new SayHelloEvent(starter, "hello to leon");

        EntityEvent<EntityEvent.Entity> entityEvent =
            new EntityEvent<>(starter, new EntityEvent.Entity("entity obj"));
        EntityEvent<EntityEvent.SimpleEntity> simpleEntityEvent =
            new EntityEvent<>(starter, new EntityEvent.SimpleEntity(" simple entity obj"));

        applicationContext.publishEvent(helloWorldEvent);
        applicationContext.publishEvent(sayHelloEvent);
        applicationContext.publishEvent(sayHelloEvent2);
        applicationContext.publishEvent(entityEvent);
        // This event would has no listener.
        applicationContext.publishEvent(simpleEntityEvent);

        applicationContext.close();
    }

    /**
     * Used for async task execute.
     */
    @Bean("taskExecutor")
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }

    /**
     * EnableHello World Event method listener.
     * <p>
     * As of Spring 4.2, you can register an event listener on
     * any public method of a managed bean by using the EventListener annotation.
     *
     * @param event the hello world event.
     */
    @EventListener
    @Order(2)
    public void processHelloWorldEvent(HelloWorldEvent event) {
        System.out.println(String.format("Method HelloWorldEvent Listener: listen the event %s.", event.getMessage()));
    }

    @EventListener
    public void processEntityEvent(EntityEvent<EntityEvent.Entity> event) {
        System.out.println(String.format("EntityEvent<EntityEvent.Entity>: listen the event %s.",
            event.getEntity().getMessage()));
    }

    /**
     * Note that if mark the {@link Async} annotation on a event listen method, the return value (other means
     * event replies) could just be <em>void</em>.
     *
     * @param event {@link EntityEvent}
     */
    @EventListener
    @Async
    public void processEntityEventAsync(EntityEvent<EntityEvent.Entity> event) {
        System.out.println(String.format("Async EntityEvent<EntityEvent.Entity>: listen the event %s.",
            event.getEntity().getMessage()));
    }

    @EventListener
    @Order(3)
    public SayHelloEvent processHelloWorldEventAndTransToSayHelloEvent(HelloWorldEvent event) {
        System.out.println(String.format("With Return Method HelloWorldEvent Listener: listen the event %s.",
            event.getMessage()));
        return new SayHelloEvent(this, event.getMessage().concat(" trans to say hello event."));
    }

    @EventListener({HelloWorldEvent.class, SayHelloEvent.class})
    @Order(5)
    public void handleHelloWorldEvent(ApplicationEvent event) {
        if (event instanceof HelloWorldEvent) {
            System.out.println(String.format("Batch Event - HelloWorldEvent: %s.",
                ((HelloWorldEvent) event).getMessage()));
        } else if (event instanceof SayHelloEvent) {
            System.out.println(String.format("Batch Event - SayHelloEvent: %s",
                ((SayHelloEvent) event).getMessage()));
        } else {
            System.out.println("No Event could be handle.");
        }
    }

    @EventListener(condition = "#event.message.contains(\"say\")")
    @Order(4)
    public void handleHelloWorldEvent(SayHelloEvent event) {
        System.out.println(String.format("Say SayHelloEvent: with given condition: %s", event.getMessage()));
    }
}
